﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.IO.Compression;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Threading;
using System.Data.SQLite;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ClanAPI
{
    class Context
    {

        private string _input;

        private int _output;



        // Constructor

        public Context(string input)
        {

            this._input = input;

        }



        // Gets or sets input

        public string Input
        {

            get { return _input; }

            set { _input = value; }

        }



        // Gets or sets output

        public int Output
        {

            get { return _output; }

            set { _output = value; }

        }

    }
    /// <summary>

    /// The 'AbstractExpression' class

    /// </summary>
    abstract class Expression
    {

        public void Interpret(Context context)
        {

            if (context.Input.Length == 0)

                return;



            if (context.Input.StartsWith(Nine()))
            {

                context.Output += (9 * Multiplier());

                context.Input = context.Input.Substring(2);

            }

            else if (context.Input.StartsWith(Four()))
            {

                context.Output += (4 * Multiplier());

                context.Input = context.Input.Substring(2);

            }

            else if (context.Input.StartsWith(Five()))
            {

                context.Output += (5 * Multiplier());

                context.Input = context.Input.Substring(1);

            }



            while (context.Input.StartsWith(One()))
            {

                context.Output += (1 * Multiplier());

                context.Input = context.Input.Substring(1);

            }

        }

        public abstract string One();

        public abstract string Four();

        public abstract string Five();

        public abstract string Nine();

        public abstract int Multiplier();

    }
    /// <summary>

    /// A 'TerminalExpression' class

    /// <remarks>

    /// Thousand checks for the Roman Numeral M

    /// </remarks>

    /// </summary>
    class ThousandExpression : Expression
    {

        public override string One() { return "M"; }

        public override string Four() { return " "; }

        public override string Five() { return " "; }

        public override string Nine() { return " "; }

        public override int Multiplier() { return 1000; }

    }
    /// <summary>

    /// A 'TerminalExpression' class

    /// <remarks>

    /// Hundred checks C, CD, D or CM

    /// </remarks>

    /// </summary>
    class HundredExpression : Expression
    {

        public override string One() { return "C"; }

        public override string Four() { return "CD"; }

        public override string Five() { return "D"; }

        public override string Nine() { return "CM"; }

        public override int Multiplier() { return 100; }

    }
    /// <summary>

    /// A 'TerminalExpression' class

    /// <remarks>

    /// Ten checks for X, XL, L and XC

    /// </remarks>

    /// </summary>
    class TenExpression : Expression
    {

        public override string One() { return "X"; }

        public override string Four() { return "XL"; }

        public override string Five() { return "L"; }

        public override string Nine() { return "XC"; }

        public override int Multiplier() { return 10; }

    }
    /// <summary>

    /// A 'TerminalExpression' class

    /// <remarks>

    /// One checks for I, II, III, IV, V, VI, VI, VII, VIII, IX

    /// </remarks>

    /// </summary>
    class OneExpression : Expression
    {

        public override string One() { return "I"; }

        public override string Four() { return "IV"; }

        public override string Five() { return "V"; }

        public override string Nine() { return "IX"; }

        public override int Multiplier() { return 1; }

    }

    public class TankTotals
    {
        public class sTank
        {
            public String _Name;
            public Int32 _Number;
            public Int32 _Level;

            public String Name
            {
                get { return _Name; }
                set { _Name = Name; }
            }
            public Int32 Number
            {
                get { return _Number; }
                set { _Number = Number; }
            }
            public Int32 Level
            {
                get { return _Level; }
                set { _Level = Level; }
            }
        }
        public List<sTank> TotalTanks = new List<sTank>();
        public void UpdateTank(Tank t)
        {
            try
            {
                Int32 i = TotalTanks.FindIndex(delegate(sTank p)
                {

                    return p._Name.ToLower() == t.Name.ToLower();

                });
                if (i == -1)
                {
                    sTank n = new sTank();
                    n._Name = t.Name;
                    n._Number = 1;
                    n._Level = t.Level;
                    TotalTanks.Add(n);
                    return;
                }
                else
                {
                    TotalTanks[i]._Number++;
                }
            }
            catch
            {
            }
        }
    }
    [Serializable()]
    public class Clan : ISerializable
    {
        public delegate void PlayerCountHandler(Object Sender, Clan mClan);
        public delegate void GetCompleteHandler(Object Sender, Boolean AllComplete, Clan mClan);

        public Int32 TotalPlayers = 0;
        public String Name = null;
        public String ClanId = null;

        public List<Player> PlayerList = new List<Player>();
        public TankTotals Totals = new TankTotals();

        public Int32 iCount = -1;
        public Int32 iTokens = 0;
        public Int32 iVal = 0;
        public Int32 iUse = 0;
        public Int32 T10Players = 0;
        public Int32 T9Players = 0;
        public Int32 T8APlayers = 0;
        public Int32 T10Tanks = 0;
        public Int32 T9Tanks = 0;
        public Int32 T8ATanks = 0;

        public Clan(Int32 Threads, String clanId)
        {
            try
            {
                Thread tMain = new Thread(new ParameterizedThreadStart(mGetClan));
                tMain.IsBackground = true;
                ClanId = clanId;
                Object[] NewArgs = { Threads, this };
                tMain.Start(NewArgs);
            }
            catch { }
        }

        public Clan(SerializationInfo info, StreamingContext ctxt)
        {
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
        }

        Player[] GetPlayerList(String ClanId, Clan mClan)
        {
            JObject o = API.GetJson("http://uc.worldoftanks.eu/uc/clans/" + ClanId + "/members/?type=table&offset=0&limit=100&order_by=name&search=&echo=1&id=clan_members_index", "http://uc.worldoftanks.eu/uc/clans/" + ClanId);
            JContainer MainData = (JContainer)o["request_data"];
            mClan.TotalPlayers = (Int32)MainData["total_count"];
            JArray ItemArray = (JArray)MainData["items"];
            List<Player> PlayerList = new List<Player>();
            foreach (JContainer b in ItemArray)
            {
                PlayerList.Add(new Player(((Int32)b["account_id"]).ToString(), (String)b["name"]));
            }
            return PlayerList.ToArray();
        }
        void GetClanName(Clan mClan)
        {
            String Html = API.GetHTML("http://uc.worldoftanks.eu/uc/clans/" + mClan.ClanId + "/");
            int start = Html.IndexOf("<div class=\"b-text-info\">");
            if (start == -1)
                return;
            String resault = Html.Substring(start);
            int end = resault.IndexOf("</h2>");
            resault = resault.Substring(0, end);
            mClan.Name = API.StripTags(resault).Trim();
        }

        void mGetClan(Object Args)
        {
            try
            {
                Clan mClan = (Clan)((Object[])Args)[1];
                mClan.PlayerList = new List<Player>();
                if (mClan.ClanId.Length < 9)
                {
                    switch (mClan.ClanId.Length)
                    {
                        case 8:
                            mClan.ClanId = "5" + mClan.ClanId;
                            break;
                        case 7:
                            mClan.ClanId = "50" + mClan.ClanId;
                            break;
                        case 6:
                            mClan.ClanId = "500" + mClan.ClanId;
                            break;
                        case 5:
                            mClan.ClanId = "5000" + mClan.ClanId;
                            break;
                        case 4:
                            mClan.ClanId = "50000" + mClan.ClanId;
                            break;
                        case 3:
                            mClan.ClanId = "500000" + mClan.ClanId;
                            break;
                        case 2:
                            mClan.ClanId = "5000000" + mClan.ClanId;
                            break;
                        case 1:
                            mClan.ClanId = "50000000" + mClan.ClanId;
                            break;
                    }
                }
                GetClanName(mClan);
                mClan.PlayerList.AddRange(GetPlayerList(mClan.ClanId, mClan));
                Thread[] t;
                List<List<Player>> Chunks;
                if (mClan.TotalPlayers >= (Int32)((Object[])Args)[0])
                {
                    Chunks = API.SplitIntoChunks(mClan.PlayerList, mClan.PlayerList.Count / (Int32)((Object[])Args)[0]);
                    t = new Thread[(Int32)((Object[])Args)[0]];
                }
                else
                {
                    t = new Thread[1];
                    Chunks = new List<List<Player>>();
                    Chunks.Add(mClan.PlayerList);
                }
                mClan.PlayerList = new List<Player>();
                for (Int32 i = 0; i < t.Length; i++)
                {
                    try
                    {
                        t[i] = new Thread(new ParameterizedThreadStart(this.mGetTanks));
                        t[i].IsBackground = true;
                        t[i].Name = i.ToString();
                        Object[] NewArgs = { Chunks[i], mClan };
                        t[i].Start(NewArgs);
                    }
                    catch { }
                }
            }
            catch { }
        }
        void mGetTanks(Object Args)
        {
            try
            {
                Clan mClan = (Clan)((Object[])Args)[1];
                List<Player> PlayerList = (List<Player>)((Object[])Args)[0];
                Int32 iTokens = 0;
                Int32 iUse = 0;
                Int32 iVal = 0;
                Int32 iT10 = 0;
                Int32 iT9 = 0;
                Int32 iT8A = 0;
                Int32 iT10Tanks = 0;
                Int32 iT9Tanks = 0;
                Int32 iT8ATanks = 0;
                foreach (Player l in PlayerList)
                {
                    try
                    {
                        l.GetPlayer(l.Id, l.Name);
                        if (l.Token)
                            iTokens++;
                        if (l.UsefulTanks > 0)
                            iUse++;
                        iVal += l.Value;
                        if (l.T10 > 0)
                        {
                            iT10++;
                            iT10Tanks += l.T10;
                        }
                        if (l.T9 > 0)
                        {
                            iT9++;
                            iT9Tanks += l.T9;
                        }
                        if (l.T8A > 0)
                        {
                            iT8A++;
                            iT8ATanks += l.T8A;
                        }
                        OnPlayerCountChange(mClan);
                    }
                    catch { }
                }
                lock (mClan)
                {
                    mClan.iTokens += iTokens;
                    mClan.iVal += iVal;
                    mClan.iUse += iUse;
                    mClan.T10Players += iT10;
                    mClan.T9Players += iT9;
                    mClan.T8APlayers += iT8A;
                    mClan.T10Tanks += iT10Tanks;
                    mClan.T9Tanks += iT9Tanks;
                    mClan.T8ATanks += iT8ATanks;
                    mClan.PlayerList.AddRange(PlayerList);
                    mClan.PlayerList.Sort(delegate(Player cust1, Player cust2)
                    {
                        return Comparer<String>.Default.Compare(cust1.Name, cust2.Name);
                    });
                }
                this.OnGetCompleteChange(mClan);
            }
            catch
            {
                this.OnGetCompleteChange(null);
            }
        }

        //Events
        public event PlayerCountHandler PlayerCountChange;
        protected void OnPlayerCountChange(Clan mClan)
        {
            if (PlayerCountChange != null)
            {
                PlayerCountChange(this, mClan);
            }
        }
        public event GetCompleteHandler PlayerCompleteChange;
        protected void OnGetCompleteChange(Clan mClan)
        {
            if (PlayerCompleteChange != null)
            {
                Boolean AllComplete = false;
                if (mClan.PlayerList.Count == mClan.TotalPlayers)
                    AllComplete = true;
                PlayerCompleteChange(this, AllComplete, mClan);
            }
        }
    }
    public class Player
    {
        public String Name;
        public String Id;
        public Int32 Value;
        public Int32 Place;
        public Int32 T10 = 0;
        public Int32 T9 = 0;
        public Int32 T8A = 0;
        public Boolean Token = false;
        public Int32 UsefulTanks = 0;
        public List<Tank> Tanks = new List<Tank>();
        public Int32 TankType = 5;
        public Int32 Team = 1;
        public Int32 Rank = 0;
        //public CashStatus stat;

        public Player(String I, String N)
        {
            this.Name = N;
            this.Id = I;
        }

        public override String ToString()
        {
            StringBuilder tmp = new StringBuilder();
            tmp.Append("Name\tID\tGlobal Rank Value\tGlobal Rank Place\r");
            tmp.Append(this.Name + "\t" + this.Id + "\t" + this.Value.ToString() + "\t" + this.Place.ToString());
            tmp.Append("\r\tTank\tLevel\tBattles\tVictories");
            foreach (Tank T in Tanks)
            {
                tmp.Append("\r\t" + T.Name + "\t" + T.Level.ToString() + "\t" + T.Battles.ToString() + "\t" + T.Victories.ToString());
            }
            return tmp.ToString();
        }
        public Int32 GetPlayer(String Id, String Name)//, CookieContainer cookieJar)
        {
            string url = "http://uc.worldoftanks.eu/uc/accounts/" + Id + "-" + Name + "/";
            String Html = API.GetHTML(url);
            int start = Html.IndexOf("<td><span>Global Rating</span></td>");
            int end = Html.IndexOf("<td class=\"abbreviation value\">W/B</td>");
            if (start == -1)
                return 0;
            String resault = Html.Substring(start, end - start - 1);
            String[] stringSeparators3 = { "</td>" };
            String[] Vals = resault.Split(stringSeparators3, StringSplitOptions.None);
            this.Value = Convert.ToInt32(API.StripTags(Vals[1]).Trim());
            String Place = API.StripTags(Vals[2]).Trim();
            if (Place != "---")
                this.Place = Convert.ToInt32(Place);
            start = Html.IndexOf("<table  class=\"t-statistic\">");
            end = Html.IndexOf("<!--sidebar-->");
            if (start == -1)
                return 0;
            resault = Html.Substring(start, end - start - 1);
            String[] stringSeparators = { "<tr>" };
            String[] Tanks = resault.Split(stringSeparators, StringSplitOptions.None);
            String[] List = { "hummel", "gw panther", "gw tiger", "gw typ e", "m41", "m12", "m40/m43", "t92", "su-8", "su-14", "object 212", "object 261" };
            String[] List1 = { "gw typ e", "t92", "object 261" };
            //String[] List2 = { "GW Tiger", "M40/M43", "Object 212" };
            //String[] List3 = { "GW Panther", "M12", "SU-14" };
            //String[] List4 = { "Hummel", "M41", "SU-8" };
            Int32 Count = 0;
            foreach (String Tank in Tanks)
            {
                String[] stringSeparators2 = { "</td>" };
                String[] T = Tank.Split(stringSeparators2, StringSplitOptions.None);
                try
                {
                    Tank TmpTank = new Tank();
                    TmpTank.Level = RomanConvert(API.StripTags(T[0]).Trim());
                    TmpTank.Name = API.StripTags(T[1]).Trim();
                    TmpTank.Battles = Convert.ToInt32(API.StripTags(T[2]).Trim());
                    TmpTank.Victories = Convert.ToInt32(API.StripTags(T[3]).Trim());
                    this.Tanks.Add(TmpTank);
                    if (TmpTank.Level > 4)
                    {
                        Count++;
                        this.Token = true;
                    }
                    if (List.Contains(TmpTank.Name.ToLower()))
                        this.UsefulTanks++;
                    else if (TmpTank.Level > 7)
                        this.UsefulTanks++;

                    if (List1.Contains(TmpTank.Name.ToLower()))
                        this.T8A++;
                    if (TmpTank.Level == 9)
                        this.T9++;
                    if (TmpTank.Level == 10)
                        this.T10++;
                }
                catch
                {
                }
            }
            this.Tanks.Sort(delegate(Tank cust1, Tank cust2)
            {
                return Comparer<int>.Default.Compare(cust1.Level, cust2.Level);
            });
            this.Tanks.Reverse();
            return Count;
        }
        Int32 RomanConvert(String roman)
        {
            Context context = new Context(roman);

            // Build the 'parse tree'
            List<Expression> tree = new List<Expression>();
            tree.Add(new ThousandExpression());
            tree.Add(new HundredExpression());
            tree.Add(new TenExpression());
            tree.Add(new OneExpression());

            // Interpret
            foreach (Expression exp in tree)
            {
                exp.Interpret(context);
            }
            return context.Output;
        }
    }
    public class Tank
    {
        Int32 _Level;
        String _Name;
        Int32 _Battles;
        Int32 _Victories;
        public Int32 Level
        {
            get
            {
                return _Level;
            }
            set
            {
                _Level = value;
            }
        }
        public String Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = String.Intern(value);
            }
        }
        public Int32 Battles
        {
            get
            {
                return _Battles;
            }
            set
            {
                _Battles = value;
            }
        }
        public Int32 Victories
        {
            get
            {
                return _Victories;
            }
            set
            {
                _Victories = value;
            }
        }
    }

    public class API
    {
        // Delegate Method Types
        

        // Private Properties
        static CookieContainer _cookieJar = new CookieContainer();
        static String _csrfToken = String.Empty;

        // Public Properties

        // Constructor, Finalize, Dispose

        // Private Methods
        static String GetcsrfToken()
        {
            HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("http://uc.worldoftanks.eu/uc/clans/");
            Request.CookieContainer = _cookieJar;
            HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
            Stream responseStream = Response.GetResponseStream();
            if (Response.ContentEncoding.ToLower().Contains("gzip"))
                responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
            StreamReader Reader = new StreamReader(responseStream, Encoding.Default);
            String Html = Reader.ReadToEnd();
            String SetCookie = Response.Headers["Set-Cookie"];
            Response.Close();
            String[] TempArry = SetCookie.Split(';');
            String csrftoken = "";
            foreach (String x in TempArry)
            {
                String[] i = x.Split('=');
                if (i[0] == "csrftoken")
                {
                    csrftoken = i[1];
                }
            }
            _cookieJar.Add(new Cookie("csrftoken", csrftoken.Trim(), "/", "uc.worldoftanks.eu"));
            return csrftoken.Trim();
        }
        static public JObject GetJson(String URL, String Referer)
        {
            HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
            Request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 FirePHP/0.5";
            Request.Accept = "application/json, text/javascript, */*; q=0.01";
            Request.Headers.Add("Accept-Language", "en-gb,en;q=0.5");
            Request.Headers.Add("Accept-Encoding", "gzip, deflate");
            Request.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            Request.Headers.Add("Keep-Alive", "115");
            Request.Headers.Add("X-Requested-With", "XMLHttpRequest");
            if (_csrfToken == String.Empty)
                _csrfToken = GetcsrfToken();
            Request.Headers.Add("X-CSRFToken", _csrfToken);
            Request.Referer = Referer;
            Request.Headers.Add("x-insight", "activate");
            Request.CookieContainer = _cookieJar;
            HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
            Stream responseStream = Response.GetResponseStream();
            if (Response.ContentEncoding.ToLower().Contains("gzip"))
                responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
            StreamReader Reader = new StreamReader(responseStream, Encoding.Default);
            string Html = Reader.ReadToEnd();
            Response.Close();
            return JObject.Parse(Html);
        }
        public static String GetHTML(String URL)
        {
            HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
            Request.CookieContainer = _cookieJar;
            HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
            Stream responseStream = Response.GetResponseStream();
            if (Response.ContentEncoding.ToLower().Contains("gzip"))
                responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
            StreamReader Reader = new StreamReader(responseStream, Encoding.Default);
            return Reader.ReadToEnd();
        }

        void GetProv()
        {
            Random tmp = new Random();
            Double t = tmp.NextDouble();
            Int32 i = (Int32)(t * 100000);
            JObject o = GetJson("http://uc.worldoftanks.eu/uc/clanwars/provinces/regions/2/?ct=json&hash=" + i.ToString(), "http://uc.worldoftanks.eu/uc/clanwars/maps/earth/reg_02/");
            JArray ItemArray = (JArray)o["provinces"];
            foreach (JContainer b in ItemArray)
            {
            }
        } //not working!

        // Private Methods
        public static string StripTags(string source)
        {
            try
            {
                char[] array = new char[source.Length];
                int arrayIndex = 0;
                bool inside = false;

                for (int i = 0; i < source.Length; i++)
                {
                    char let = source[i];
                    if (let == '<')
                    {
                        inside = true;
                        continue;
                    }
                    if (let == '>')
                    {
                        inside = false;
                        continue;
                    }
                    if (!inside)
                    {
                        array[arrayIndex] = let;
                        arrayIndex++;
                    }
                }
                return (new string(array, 0, arrayIndex)).Replace("&nbsp;", ""); ;
            }
            catch { return null; }
        }
        public static List<List<T>> SplitIntoChunks<T>(List<T> list, int chunkSize)
        {
            if (chunkSize <= 0)
            {
                throw new ArgumentException("chunkSize must be greater than 0.");
            }

            List<List<T>> retVal = new List<List<T>>();
            int index = 0;
            while (index < list.Count)
            {
                int count = list.Count - index > chunkSize && list.Count - index - chunkSize >= chunkSize ? chunkSize : list.Count - index;
                retVal.Add(list.GetRange(index, count));

                index += count;
            }

            return retVal;
        }
    }
}
